package database.relation;

import database.Schema;
import database.attset.SetAttribute;
import database.fd.FunctionalDependency;
import database.fd.ListFunctionDependency;

import java.util.ArrayList;

@SuppressWarnings("serial")
public class Relation extends SetAttribute {
    private ListFunctionDependency listFd;
    private ArrayList<SetAttribute> keys;
    private SetAttribute nonPrimeAtt;
    private SetAttribute primeAtt;
    private String rname;
    
    private int _2NF = 0;
    private int _3NF = 0;
    private int _BCNF = 0;
    private SetAttribute _2NFRedundance;
    private SetAttribute _3NFRedundance;
    private SetAttribute _BCNFRedundance;
    private ListFunctionDependency _2NFViolentFDs;
    private ListFunctionDependency _3NFViolentFDs;
    private ListFunctionDependency _BCNFViolentFDs;

    public String getRname() {
        return rname;
    }

    public void setRname(String rname) {
        this.rname = rname;
    }
    
    public void resetAll() {
        listFd = null;
        keys = null;
        nonPrimeAtt = null;
        primeAtt = null;
        
        _2NF = 0;
        _3NF = 0;
        _BCNF = 0;
        
        _2NFRedundance = null;
        _3NFRedundance = null;
        _BCNFRedundance = null;
        _2NFViolentFDs = null;
        _3NFViolentFDs = null;
        _BCNFViolentFDs = null;
    }


    /**
     * Kiem tra 1 tap thuoc tinhh set (Attribute) co phai la Super Key cua quan he (Relation) dang xet khong.
     * @param set tap thuoc tinh muon ktra
     * @return true neu set la superkey cua thi, nguoc lai la false
     */
    public boolean isSK(SetAttribute set){
        boolean isSK = false;
        int i = 0;
        if (keys==null){
            this.getKeys();
        }
        while (!isSK && i<keys.size()){

            if (set.isSuperSet(keys.get(i))){
                isSK = true;
            } else {
                i++;
            }
        }

        return isSK;
    }


    public SetAttribute getNonPrimeAtt() {
        if (nonPrimeAtt == null){
            getKeys();
        }
        return nonPrimeAtt;
    }


    public SetAttribute getPrimeAtt() {
        if (primeAtt == null){
            getKeys();
        }
        return primeAtt;
    }
    
    /*
     * neu chua co khoa thi chay giai thuat tim khoa
     * dong thoi tim luon non-prime attribute
     */
    public ArrayList<SetAttribute> getKeys() {
        if(keys == null) {
            //chay giai thuat tim khoa va nonPrimeAtt
            // giai thuat tim khoa
            keys = new ArrayList<SetAttribute>();
            
            SetAttribute allRight = new SetAttribute();
            SetAttribute allLeft = new SetAttribute();
            SetAttribute N = null;
            SetAttribute closureN = null;

            // tim allLeft va allRight
            for (int i = 0; i < Schema.listFunctionDependency.size(); i++){
                FunctionalDependency fd = Schema.listFunctionDependency.get(i);
                allLeft = allLeft.union(fd.getLeft());
                allRight = allRight.union(fd.getRight());
            }

            // tim tap N vao bao dong cua no
            N = this.difference(allRight);
            closureN = N.getClosure(Schema.listFunctionDependency);

            if (closureN.isSuperSet(this)){
                keys.add(N);
            } else {
                SetAttribute D = allRight.difference(allLeft);
                SetAttribute L = this.difference(D.union(closureN));
                
                // dung de qui cho Li
                this.participateToGetKeys(N, L, closureN, Schema.listFunctionDependency);

                int j = 0;
                int k = 0;
                while (j < keys.size()){
                    SetAttribute tmp = keys.get(j);
                    int life = 1;
                    k = 0;
                    while (k < keys.size() && life >= 0){
                        if (keys.get(k).isSubSet(tmp)){
                            life--;
                        }
                        k++;
                    }
                    if (life < 0){
                        keys.remove(j);
                    } else {
                        j++;
                    }
                }

                if (keys.isEmpty()){
                    keys.add((SetAttribute) this.clone());
                }
            }

            // lay non-prime
            primeAtt = new SetAttribute();
            for (int i=0; i<keys.size(); i++){
                primeAtt = primeAtt.union(keys.get(i));
            }
            nonPrimeAtt = this.difference(primeAtt);
        }

        return keys;
    }
    
    /**
     * 
     * @param initAtts
     * @param listAtts
     * @param initClosure
     * @param listFd
     */
    public void participateToGetKeys(
            SetAttribute initAtts,
            SetAttribute listAtts,
            SetAttribute initClosure,
            ListFunctionDependency listFd){

        // tmp_listAtts se bi thay doi
        SetAttribute tmp_listAtts = (SetAttribute) listAtts.clone();
        for (int i=0; i<listAtts.size(); i++){
            SetAttribute atts = (SetAttribute) initAtts.clone();
            SetAttribute closure = (SetAttribute) initClosure.clone();
            ListFunctionDependency tmp_listFd = (ListFunctionDependency) listFd.clone();

            atts.add(listAtts.get(i));
            closure.add(listAtts.get(i));

              // tinh clouser tu clouser cu va tap phu thuoc ham da thu giam
            closure = closure.getClosure_Extra(tmp_listFd);
            if (closure.isSuperSet(this)){
                keys.add(atts);
            } else {
                if (!tmp_listFd.isEmpty()){
                    SetAttribute remain = tmp_listAtts.difference(closure);
                    this.participateToGetKeys(atts, remain, closure, tmp_listFd);
                }
            }
            tmp_listAtts.remove(0);
        }
    }


    public ListFunctionDependency getListFd(){
        if (this.listFd==null){
            setListFd();
            this.listFd = Schema.getMinimalCoverFD(this.listFd);
        }
        return listFd;
    }


    /**
     *
     */
    private void setListFd(){
        listFd = new ListFunctionDependency();
        ListFunctionDependency tmp_FDs = (ListFunctionDependency) Schema.listFunctionDependency.clone();
        SetAttribute initAtts = new SetAttribute();
        participateToSetListFD(initAtts, this, initAtts, tmp_FDs);
    }
    private void participateToSetListFD(
            SetAttribute initAtts,
            SetAttribute listAtts,
            SetAttribute initClosure,
            ListFunctionDependency listFd){

        SetAttribute tmp_listAtts = (SetAttribute) listAtts.clone();
        for (int i=0; i<listAtts.size(); i++){
            SetAttribute right = null;
            SetAttribute atts = (SetAttribute) initAtts.clone();
            SetAttribute closure = (SetAttribute) initClosure.clone();
            ListFunctionDependency tmp_listFd = (ListFunctionDependency) listFd.clone();

            atts.add(listAtts.get(i));
            closure.add(listAtts.get(i));

              // tinh clouser tu clouser cu va tap phu thuoc ham da thu giam
            closure = closure.getClosure_Extra(tmp_listFd);
              //
            right = closure.difference(initClosure).intersection(this);
            right.remove(Integer.valueOf(listAtts.get(i)));
            if (!right.isEmpty()){
                FunctionalDependency tmp_FD = new FunctionalDependency();
                tmp_FD.setLeft(atts);
                tmp_FD.setRight(right);
                this.listFd.add(tmp_FD);
            } else {
                if (!tmp_listFd.isEmpty()){
                    SetAttribute remain = tmp_listAtts.difference(closure);
                    this.participateToSetListFD(atts, remain, closure, tmp_listFd);
                }
            }
            tmp_listAtts.remove(0);
        }
    }
    
    /*
     * - lay list key
     * - lay set non-prime atribute
     * - loop list key
     * -    - neu khoa chi co 1 att thi tiep tuc loop
     *      - neu khoa co nhieu hon thi lay list tap con lon nhat cua khoa roi tim bao dong
     *          - neu moi phan tu cua tap non-prime attibute thuoc bao dong thi return false
     */
//    public boolean is2NormalForm() {
//        if (_2NF == 1){
//            return true;
//        } else if (_2NF == -1){
//            return false;
//        }
//        _2NF = 1;
//        _2NFRedundance = new SetAttribute();
//
//        getKeys();
//        boolean is2NF = true;
//        SetAttribute closure = null;
//        ArrayList<SetAttribute> lsSubSet = null;
//
//        int i = 0;
//        while (i < keys.size()){
//            lsSubSet = keys.get(i).getListMaxSubset();
//            int j = 0;
//            while (j < lsSubSet.size()){
//                closure = lsSubSet.get(j).getClosure(Schema.listFunctionDependency);
//                  // lay clouser giao voi tap nonPrime. Neu ket qua khac rong
//                  // thi vi ham chuan 2 va cac thuoc tinh vi pham se nam trong tap giao do
//                SetAttribute vipham = closure.intersection(this.nonPrimeAtt);
//                if (vipham.size()>0){
//                    _2NFRedundance = _2NFRedundance.union(vipham);
//                    is2NF = false;
//                    _2NF = -1;
//                }
//                j++;
//            }
//            i++;
//        }
//
//        return is2NF;
//    }


    /**
     * bien _2NF cho biet da kiem tra dang chuan 2 chua, neu chua moi tien hanh kiem tra.
     * 1. kiem tra dang chuan 2.
     * 2. bien _2NFViolenrFD se duoc khoi tao va chua cac phu thuoc ham vi pham.
     * 3. bien _2NFRedundance se duoc khoi tao va chua cac thuoc tinh du thua.
     * @return true neu relation dat dang chuan 2, false neu khong dat.
     */
    public boolean is2NormalForm() {
        if (_2NF == 1){
            return true;
        } else if (_2NF == -1){
            return false;
        }
        _2NF = 1;
        _2NFRedundance = new SetAttribute();
        _2NFViolentFDs = new ListFunctionDependency();

        boolean is2NF = true;
        this.getKeys();
        ListFunctionDependency lsFD = this.getListFd();
        
          // lap tat ca cac FD co trong relation
        for (FunctionalDependency fd : lsFD){
            SetAttribute left = fd.getLeft();

              // kiem tra voi tung candidate key xem ve trai cua fd co phai la con thuc su khong
            for (SetAttribute key : this.keys){
                  // neu left la con thuc su cua key thi xet tiep ve phai
                if (left.isRealSubSet(key)){
                    SetAttribute right = fd.getRight();
                    SetAttribute vipham = right.intersection(this.nonPrimeAtt);
                      // vi pham la tap giao cua ve trai va tap thuoc tinh khong khoa
                      // vipham.size() > 0 tuc la co chua thuoc tinh khong khoa
                    if (vipham.size()>0){
                        _2NFViolentFDs.add(fd.clone());
                        _2NFRedundance = _2NFRedundance.union(vipham);
                        is2NF = false;
                        _2NF = -1;
                    }
                }
            }
        }

        return is2NF;
    }

    public ListFunctionDependency get2NFViolentFDs(){
        if (_2NFViolentFDs==null){
            is2NormalForm();
        }
        ListFunctionDependency returnLsFD = Schema.mergeFD(_2NFViolentFDs);
        return returnLsFD;
    }

    public SetAttribute get2NFRedundance(){
        if (_2NFRedundance==null){
            is2NormalForm();
        }
        return _2NFRedundance;
    }


    /**
     * bien _3NF cho biet da kiem tra dang chuan 3 chua, neu chua moi tien hanh kiem tra.
     * 1. kiem tra dang chuan 3.
     * 2. bien _3NFViolenrFD se duoc khoi tao va chua cac phu thuoc ham vi pham.
     * 3. bien _3NFRedundance se duoc khoi tao va chua cac thuoc tinh du thua.
     * @return true neu relation dat dang chuan 3, false neu khong dat.
     */
    public boolean is3NormalForm() {
        if (_3NF == 1){
            return true;
        } else if (_3NF == -1){
            return false;
        }
        _3NF = 1;
        _3NFRedundance = new SetAttribute();
        _3NFViolentFDs = new ListFunctionDependency();

        boolean is3NF = true;
        this.getKeys();
        ListFunctionDependency lsFD = this.getListFd();

        for (FunctionalDependency fd : lsFD){
            SetAttribute left = fd.getLeft();
              // neu ve trai ko la SK thi pha kiem tra tiep ve phai
            if (!this.isSK(left)){
                SetAttribute right = fd.getRight();
                SetAttribute vipham = right.intersection(this.nonPrimeAtt);
                if (vipham.size()>0){
                    _3NFViolentFDs.add(fd.clone());
                    _3NFRedundance = _3NFRedundance.union(vipham);
                    is3NF = false;
                    _3NF = -1;
                }
            }
        }

        return is3NF;
    }


    public ListFunctionDependency get3NFViolentFDs(){
        if (_3NFViolentFDs==null){
            is2NormalForm();
        }
        ListFunctionDependency returnLsFD = Schema.mergeFD(_3NFViolentFDs);
        return returnLsFD;
    }

    public SetAttribute get3NFRedundance(){
        if (_3NFRedundance==null){
            is2NormalForm();
        }
        return _3NFRedundance;
    }


    /**
     * bien _BCNF cho biet da kiem tra dang chuan BCK chua, neu chua moi tien hanh kiem tra.
     * 1. kiem tra dang chuan BCK.
     * 2. bien _BCNFViolenrFD se duoc khoi tao va chua cac phu thuoc ham vi pham.
     * 3. bien _BCNFRedundance se duoc khoi tao va chua cac thuoc tinh du thua.
     * @return true neu relation dat dang chuan BCK, false neu khong dat.
     */
    public boolean isBCNormalForm() {
        if (_BCNF == 1){
            return true;
        } else if (_BCNF == -1){
            return false;
        }
        _BCNF = 1;
        _BCNFRedundance = new SetAttribute();
        _BCNFViolentFDs = new ListFunctionDependency();

        boolean isBCNF = true;
        this.getKeys();
        ListFunctionDependency lsFD = this.getListFd();

        for (FunctionalDependency fd : lsFD){
            SetAttribute left = fd.getLeft();
              // neu ve trai ko la SK thi pha kiem tra tiep ve phai
            if (!this.isSK(left)){
                SetAttribute right = fd.getRight();
                SetAttribute vipham = right.intersection(this.nonPrimeAtt);
                if (vipham.size()>0){
                    _BCNFViolentFDs.add(fd.clone());
                    _BCNFRedundance = _BCNFRedundance.union(vipham);
                    isBCNF = false;
                    _BCNF = -1;
                }
            }
        }

        return isBCNF;
    }


    public ListFunctionDependency getBCNFViolentFDs(){
        if (_BCNFViolentFDs==null){
            isBCNormalForm();
        }
        ListFunctionDependency returnLsFD = Schema.mergeFD(_BCNFViolentFDs);
        return returnLsFD;
    }

    public SetAttribute getBCNFRedundance(){
        if (_BCNFRedundance==null){
            isBCNormalForm();
        }
        return _BCNFRedundance;
    }

}
